/*
    文件工具类：对文件进行操作
		功能：
			1、获取文件大小
			2、获取文件最后修改时间
			3、获取文件最后访问时间
			4、从文件路径中获取文件名称
			5、向文件中写入数据
			6、读取文件数据
			7、读取文件指定区间的数据
			8、判断文件是否存在
			9、创建目录
			10、遍历目录
			11、压缩文件
			12、解压文件
*/

#ifndef __M_UTIL_H__
#define __M_UTIL_H__

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include "sup/sup_h/bundle.h"




using namespace std;

namespace cloud{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    public:
        FileUtil(const string &filename):_filename(filename){

        }

        int64_t fileSize(){
            struct stat st;
            //通过文件路径获取文件属性，放入st
            if(stat(_filename.c_str(), &st) < 0){
                cerr <<" get file size failed!" << endl;
                return -1;
            }
            return st.st_size;
        }

        time_t lastModifyTime(){
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0){
                cerr << "get last update time failed!" << endl;
                return -1;
            }
            return st.st_mtim.tv_sec;
        }

        time_t lastAccessTime(){
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0){
                cerr << "get last access time failed!" << endl;
                return -1;
            }
            return st.st_atim.tv_sec;
        }

        string fileName(){
            // ./abc/test.txt
            //从后往前找到第一个/
            size_t pos = _filename.find_last_of('/');
            if(pos == string::npos){
                return _filename;
            }
            return _filename.substr(pos + 1);
        }

        //获取文件区间数据
        bool getSection(string *body, size_t pos, size_t len){

            //检查读取区间
            size_t fsize = fileSize();
            if(pos+len > fsize){
                cerr << "get len too long!" << endl;
                return false;
            }

            //打开文件
            ifstream ifs;
            ifs.open(_filename, ios::binary);
            if(ifs.is_open() == false){
                cerr << "ifs.open() file failed!" << endl;
                return false;
            }

            //跳转到指定区间
            ifs.seekg(pos, ios::beg);
            //初始化body
            body->resize(len);
            //读取len个字节到body
            ifs.read(&(*body)[0], len);
            if(ifs.good() == false){
                cerr << _filename << "ifs.read() failed!" << endl;
                ifs.close();
                return false;
            }

            ifs.close();
            return true;
        }

        //获取文件所有内容
        bool getContent(string *body){
            size_t fsize = fileSize();
            return getSection(body, 0, fsize);
        }

        //写入数据
        bool setContent(const string &body){
            ofstream ofs;
            ofs.open(_filename, ios::binary);
            if(ofs.is_open() == false){
                cerr << "ofs.open() file failed!" << endl;
                return false;
            }

            ofs.write(&body[0], body.size());
            if(ofs.good() == false){
                cerr << "ofs.write failed!" << endl;
                ofs.close();
                return false;
            }
            
            ofs.close();
            return true;
        }

        //压缩
        bool compress(const string &packname){
            // v1.读取文件
            string body;
            if(getContent(&body) == false){
                cerr << "compress get file content failed!" << endl;
                return false;
            }

            // 2.进行压缩
            string packed = bundle::pack(bundle::LZIP, body);

            // 3.将压缩的数据存储到压缩包中
            FileUtil fu(packname);
            if (fu.setContent(packed) == false){
                cerr << "compress write packed data failed!" << endl;
            }
            return true;
        }

        //解压
        bool uncompress(const string &filename){
            // 1.读取文件
            string body;
            if(getContent(&body) == false){
                cerr << "compress get file content failed!" << endl;
                return false;
            }

            // 2.解压
            string unpacked = bundle::unpack(body);

            // 3.将解压后的文件写入到新文件
            FileUtil fu(filename);
            if (fu.setContent(unpacked) == false){
                cerr << "compress write packed data failed!" << endl;
            }
            return true;
        }

        //判断文件是否存在
        bool isExist(){
            return fs::exists(_filename);
        }

        //创建目录
        bool createDirectory(){
            if(isExist()){
                return true;
            }
            return fs::create_directories(_filename);
        }

        //遍历目录，将目录中的文件添加到vector中
        bool scanDirectory(vector<string> *arr){
            //用接口中的迭代器遍历
            for(auto& p : fs::directory_iterator(_filename)){
                //如果是个目录而不是文件则继续遍历，如果是个文件则添加到vector
                if(fs::is_directory(p) == false){
                    //将p指向的目录中的相对路径提取出来，并转化成字符串，插入到vector
                    arr->push_back(fs::path(p).relative_path().string());
                }
            }
            return true;
        }

        //删除文件
        bool remove(){
            if(isExist() == false){
                return true;
            }
            std::remove(_filename.c_str());
            return true;
        }

    private:
        string _filename;
    };

    //对json进行封装
    class JsonUtil{
        public:
            static bool serialize(const Json::Value &root, string *str){
                Json::StreamWriterBuilder swb;
                //用建造者建造Json::StreamWriter对象，保存在智能指针中
                unique_ptr<Json::StreamWriter> psw(swb.newStreamWriter());
                //创建流，用来保存序列化后的信息
                stringstream ss;
                //将信息序列化，并写入流
                if(psw->write(root, &ss) != 0){
                    cerr << "json wirte failed!" << endl;
                    return false;
                }
                //读取流中的数据，放到str中
                *str = ss.str();
                return true;
            }

            static bool unserialize(const string &str, Json::Value *root){
                Json::CharReaderBuilder crb;
                unique_ptr<Json::CharReader> pcr(crb.newCharReader());
                string errstr;
                ////将str.c_str()进行反序列化后存放到root中，错误信息存放到errstr中
                bool ret = pcr->parse(str.c_str(), str.c_str() + str.size(), root, &errstr);
                if(ret == false){
                    cout << "parse err" << endl;
                    return false;
                }
                return true;
            }
    };
}

//读写锁RAII加锁
enum WorR{
    WRITE,
    READ
};

class RWLock {
public:
    RWLock(pthread_rwlock_t* rwlock, WorR flag) : _rwlock(rwlock) {
        if (flag == WRITE) {
            pthread_rwlock_wrlock(_rwlock);
        } else if(flag == READ){
            pthread_rwlock_rdlock(_rwlock);
        }else{
            cerr << "WorR value is err!" << endl;
        }
    }

    ~RWLock() {
        pthread_rwlock_unlock(_rwlock);
    }

private:
    pthread_rwlock_t* _rwlock;
};

// pthread_rwlock_t _rwlock;
// RWLock _lock(&_rwlock, WorR::WRITE);    // 写锁
// RWLock _lock(&_rwlock, WorR::READ);     // 读锁


#endif